package com.google.code.dajly.model;

import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;

import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;

import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedInput;

/** Classe che rappresenta un Feed 
 * @author stefano pigozzi*/
public class Feed{

	// Data Structure
	private URL feedURL;
	private ArrayList<Entry> entries = new ArrayList<Entry>();
	
	private String title;
	private String description;
	private URL icon;
	
	private ArrayList<String> locations;
	private ArrayList<String> businesses;
	
	private FilterType filter;
	
	// Rome XML/Atom Parser Feed Objects
	private SyndFeedInput _input;
	private SyndFeed _feed;
	
	/***Aggiornamento del Feed
	 * @throws IOException Feed non raggiungibile
	 * @throws MalformedURLException URL non valido*/
	protected void refreshFeed() throws IOException,MalformedURLException{
		
		_input = new SyndFeedInput();
        try {
        	
        	InputStreamReader _i = new InputStreamReader(this.feedURL.openStream());
        	String _xml = IOUtils.toString(_i);
        	// tolgo i caratteri che fanno incazzare XCERSES!
        	_xml.replaceAll(Character.toString((char) 0x1a), "");
        	_xml.replaceAll(Character.toString((char) 0x1d), "");
        	_xml.replaceAll(new String(Character.toChars(0x1d)), "");
        	_feed = _input.build(new InputStreamReader(IOUtils.toInputStream(_xml)));
        	
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (FeedException e) {
			e.printStackTrace();
		} catch (IOException e) {
			System.out.println(this.feedURL + " non trovato, termino la procedura");
			return; 
			// Nel caso non riesca a caricare il feed esco gentilmente tanto 
			// questa procedura viene ripetuta a intervalli regolari
		}

		// setting common Feed infos
		try {
			this.title = _feed.getTitle();
		}catch (NullPointerException e){
			System.out.println(this+": titolo mancante");
		}

		try {
			this.description = _feed.getDescription();
		}catch (NullPointerException e){
			System.out.println(this+": descrizione mancante");
		}

		try {
			this.icon = new URL(_feed.getImage().getUrl()); 
		}catch (NullPointerException e){
			System.out.println(this+": icona mancante");
		}
		
		// importing Entries into data Model
        SyndEntryImpl _e;
        List<?> _l = _feed.getEntries();
        
        
        for (int i=0 ; i < _l.size() ; i++){
        	_e = (SyndEntryImpl) _l.get(i);
    		try {
    			Entry _entry = new Entry(
            			_e.getTitle(),
            			_e.getDescription().getValue(),
            			_e.getAuthor(),
            			new URL(_e.getLink()),
            			_e.getPublishedDate());
    			if(!entries.contains(_entry)){
    				if(Model.INIT)
    					entries.add(_entry);
    				else
    					entries.add(0, _entry);
    				System.out.println("added to "+this+" entry: "+_entry);
    			}
    		}catch(NullPointerException e){
    			Entry _entry = new Entry(
            			_e.getTitle(),
            			"",
            			_e.getAuthor(),
            			null,
            			_e.getPublishedDate());
    			if(!entries.contains(_entry)){
    				if(Model.INIT)
    					entries.add(_entry);
    				else
    					entries.add(0, _entry);
    			}
    		}catch (MalformedURLException e) {
        		//If the entry feed URL for full story is malformed I want to cry! really..
    			Entry _entry = new Entry(
            			_e.getTitle(),
            			_e.getDescription().getValue(),
            			_e.getAuthor(),
            			null,
            			_e.getPublishedDate());
    			if(!entries.contains(_entry)){
    				if(Model.INIT)
    					entries.add(_entry);
    				else
    					entries.add(0, _entry);
    			}
        	}
        }
               
        
        // Parsing again with JDom, and trying to get the fields in our custom namespace
        SAXBuilder builder = new SAXBuilder();
		try {
			Document doc = builder.build(feedURL.openStream());
			Namespace ns = Namespace.getNamespace("dajly", 
			"http://code.google.com/p/dajly/wiki/namespace");
			
			// Getting Locations
			try {
				String _locs = doc.getRootElement().getChild("channel").getChild("location", ns).getText();

				if (_locs != null){
					this.locations = new ArrayList<String>();
					for(String _location : _locs.split(";"))
						this.locations.add(_location);
				}
			}catch(NullPointerException e){
				System.out.println(this + ": no custom location");
			}

			// Getting Businesses
			try {
				String _buss = doc.getRootElement().getChild("channel").getChild("business", ns).getText();

				if (_buss != null){
					this.businesses = new ArrayList<String>();
					for(String _business : _buss.split(";"))
						this.businesses.add(_business);
				}
			}catch(NullPointerException e){
				System.out.println(this + ": no custom business");
			}
	
		
		} catch (JDOMException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		

	}
	
	/***Imposta il filtro
	 * @param filter [FilterType]: tipo di filtro*/
	protected void setFilter(FilterType filter){
		this.filter = filter;
	}
	
	/***@param feedURL [string]: url del Feed da costruire*/
	protected Feed(String feedURL) throws MalformedURLException,IOException{
		this.feedURL = new URL(feedURL);
		this.refreshFeed();
	}
	
	/***@return titolo*/
	public String getTitle(){
		return this.title;
	}
	
	/***@return descrizione*/
	public String getDescription(){
		return this.description;
	}
	
	/***@return url*/
	public URL getURL(){
		return this.feedURL;
	}
	
	/***@return icona*/
	public URL getIconURL(){
		return this.icon;
	}
	
	/***Ritorna una lista di Entries del Feed. ATTENZIONE: stessa lista interna alla classe, NON modificare!
	 * @return lista di entries*/
	public List<Entry> getEntries(){
		if (this.filter == null || this.filter.getFilter() == null)
			return entries;
		else { 	// assumendo di avere n := numero di entries >>> numero di filtri 
			// e business e locations l'algoritmo è circa di complessità Θ(n)
			boolean __flag = false;
			// constructing result
			ArrayList<Entry> _result = new ArrayList<Entry>();
			for(Entry _e : entries){
				__flag = false;
				for (String _f : this.filter.getFilter()){
					if(this.filter.isTitle() && _e.getTitle().toLowerCase().contains(_f.toLowerCase()) ||
							this.filter.isAuthor() && _e.getAuthor().toLowerCase().contains(_f.toLowerCase())||
							this.filter.isDescription() && _e.getMessage().toLowerCase().contains(_f.toLowerCase())){
						__flag = true;
					}
					if(hasBusiness())
						for(String _b : getBusinesses())
							if(_b.contains(_f))
								__flag = true;
					if(hasLocation())
						for(String _l : getLocations())
							if(_l.contains(_f))
								__flag = true;

					if ( __flag )
						_result.add(_e);
				}
			}
			return _result;
		}
	}
	
	/***Verifica se il feed contiene la entry
	 * @param entry [Entry]: entry da cercare
	 * @return contiene la entry?*/
	public boolean contains(Entry entry){
		return entries.contains(entry);
	}
	
	/***@return E' un feed di tipo esteso?*/
	public boolean isCustom(){
		return (this.locations != null || this.businesses != null);
	}
	
	/***@return Il feed ha il campo location?*/
	public boolean hasLocation(){
		return (this.locations != null);
	}
	
	/***@return Il Feed ha il campo business?*/
	public boolean hasBusiness(){
		return (this.businesses != null);
	}
	
	/***@return Lista di Locazioni*/
	public List<String> getLocations(){
		return this.locations;
	}
	
	/***@return Lista di business*/
	public List<String> getBusinesses(){
		return this.businesses;
	}
	
	public boolean equals(Object object){
		if (object instanceof Feed)
			return equals((Feed)object);
		else
			return false;
	}
	
	/***Controlla l'uguaglianza dei Feed
	 * @param feed [Feed]: feed da controllare
	 * @return il titolo oppure la url del feed sono uguali?*/
	public boolean equals(Feed feed){
		return this.title.equals(feed.title) || this.feedURL.equals(feed.feedURL);
	}
	
	/***Rappresentazione in Stringa
	 * @return Feed@$hash :: $title*/
	public String toString(){
		return "Feed@"+this.getClass().hashCode()+" :: "+this.getTitle();
	}
	
	
}
